home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Disc to the Future 2
/
Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin
/
MAC
/
THINKC
/
4_0
/
GNUUCP_2
/
SOURCE
/
GIO.C
< prev
next >
Wrap
Text File
|
1990-07-16
|
25KB
|
918 lines
/*
* @(#)gio.c 1.8 87/09/29 Copyright 1987 Free Software Foundation, Inc.
*
* Copying and use of this program are controlled by the terms of the
* GNU Emacs General Public License.
*
* Derived from:
* i[$]uuslave.c 1.7 08/12/85 14:04:20
* which came from the ACGNJ BBS system at +1 201 753 9758. Original
* author unknown.
*/
#ifndef lint
char gio_version[] = "@(#)gio.c Fort Pond Research-2.6 7/15/90";
#endif
#include "includes.h" /* All the system header files */
#include "uucp.h"
extern int errno;
/*
This program implements the uucp (Unix-to-Unix CoPy) protocol as a
slave (the recipient of a phone call from another Unix system) or, now,
a master (the originator of a phone call). This
protocol is used to transfer mail, files, and Usenet news from Unix
machine to Unix machine. UUCP comes with Unix (unless you get a
sleazeoid version like Xenix, where they charge you extra for it). You
can buy a commercial program for MSDOS, called UULINK, which also
implements this protocol. UULINK costs $300 and you don't get sources,
though.
The protocol requires a full 8-bit data path with no characters inserted
or deleted (e.g. ^S and ^Q are used as DATA characters). Simple serial
ports and modems do this; most complicated networks do not, at least without
setting up odd modes and such. Telenet's PC Pursuit works fine though.
The basic flow of the protocol is that the calling machine will send down
a line of text saying what it wants to do (send a file, receive a file,
or hang up). (The lines of text are encapsulated into packets; see below.)
The called machine responds with a "yes" or "no" answer, and if the answer
was yes, it sends or receives the file. Files are terminated with a
packet containing 0 bytes of data. Then the system that received the file
sends a "copy succeeded" or "copy failed" line to the other end, and
they go back to "what do we do now". A request to hang up should be
answered "no" if the called machine has some mail or files it wants to
send to the calling machine; the two machines reverse roles and the calling
machine goes into "what do we do now". If a hangup request is answered "yes",
the call is terminated.
The data flow described above is actually sent in packets containing
checksums and acknowledgements. Each packet can either hold a short
control message, e.g. an ack, or a data block. The data blocks are
numbered with a 3-bit counter, and sent with a checksum. If the sender
has not received an acknowledgement for a data block within a certain
time, it retransmits the block. The size of a data block is negotiated
at the start of a call. To send a block with fewer bytes, a "short
data" block is sent, which is just as big as a "long data" block, but
contains a 1- or 2-byte count of "how many bytes in this block are just
padding". This is a cute trick since it always works (e.g. if you want
to send 1023 out of 1024 bytes, you only need one byte for the count;
while if you want to send 1 byte out of 1024 then you have enough space
for the count to be 2 bytes).
The short control messages are used to start the call and negotiate the
packet size and the "window size", to acknowledge or reject packets,
and to terminate the packet protocol at the end of a call. The window
size is how many packets one side can send before it will stop and wait
for an acknowledgement from the other side. A window size of 1 makes
for a half-duplex protocol (which is what uuslave currently
implements), but also makes it easy to implement on micros that don't
handle serial lines with interrupts. In window 1, you just keep
sending the same packet until the other side acknowledges it. Unix
always uses a window size of 3, which is the max that can be dealt with
given the 3-bit packet numbers (for reasons that would take more space
than I want to spend here). This gives much better throughput, but
requires full duplex serial port handling and more complicated
acknowledgement strategies.
At the low level, full 8-bit bytes are sent out and received on an async serial
port. The received data is scanned for a DLE (hex 10) which indicates the
start of a packet, and the next 5 bytes are read and checked. If they
pass, this is a good packet and it is acted upon. (If it's a data
packet, we have to read in and check the data part too.) If the checks
fail, all the bytes read so far should be scanned for another DLE.
*/
/*
* UUCP "g" procotol definitions
*/
#define MAGIC 0125252 /* checksum is subtracted from this */
/*
* What is sent from one machine to the other is a control byte and
* sometimes a data block following.
* A packet is a just this, with a frame around the control byte and the
* data, if any, after the frame. The frame is 6 bytes long, and looks like:
* DLE K C0 C1 C X
* where:
* DLE is a literal ASCII DLE (Data Link Escape) character
* K is binary, 9 for a control packet, or the packet size log 2, minus 4
* e.g. K = 2 means 64 byte packet since (K+4) is 6 or 64.
* 2 2
* C0 and C1 are low, high order checksums for the entire data section, or
* are low, high order of (MAGIC minus the control byte).
* C is the control byte for the message.
* X is the xor of K, C0, C1, and C.
* If a packet does not satisfy all of the above checks, it is invalid.
*/
#define DLE 0x10 /* Start of packet indicator */
#define LENBYTE 1 /* Byte offset from DLE to length */
#define CBYTE 4 /* Byte offset from DLE to control */
#define FRAMEBYTE 5 /* Byte offset from DLE to end of frame */
#define KCONTROL 9 /* K value for control packets */
/*
* A control byte is split into three bit fields: type, x, and y.
* TT XXX YYY
* Here are the types:
*/
#define CONTROL 0 /* Control message */
#define ALTCHN 1 /* Alternate channel message, unused in UUCP */
#define LONGDATA 2 /* Long data block -- full size spec'd by K */
#define SHORTDATA 3 /* Short data block -- first byte or two is
count. Full K-size packet is sent, even
though the useful data is shorter. */
char *tt_pr[] = {"CONTROL", "ALTCHN", "LONGDATA", "SHORTDATA"};
/* If TT == CONTROL (also K will == KCONTROL) then
the x field is: and the Y field means: */
#define CLOSE 1 /* End of communication */
#define RJ 2 /* Reject packet last good packet # seen */
#define SRJ 3 /* Selective reject seq # of bad packet, resend
SRJ is not used by UUCP. */
#define RR 4 /* Receiver Ready last good packet # seen */
#define INITC 5 /* Init phase C window size to hand sender */
#define INITB 6 /* Init phase B max data segment size (K val)
to hand sender */
#define INITA 7 /* Init phase A window size to hand sender */
char *ctrl_pr[] = {"*ZERO*",
"CLOSE", "RJ", "SRJ", "RR", "INITC", "INITB", "INITA"};
/*
* If TT == LONGDATA or SHORTDATA then x field is the sequence # of this packet
* and y field is the last good packet # seen.
*
* In both data and RJ/RR packets, the "last good packet # seen" starts off
* as zero.
*/
#define MAX_PACKET 4096
unsigned char msgi[MAX_PACKET+SLOP], /* Incoming packet */
msgo[MAX_PACKET+SLOP]; /* Outgoing packet */
int firstslave, /* First packet of slave's session */
msgsize, /* Size of data part of msg */
tt, xxx, yyy, /* Fields from control byte */
rseq, /* Last good packet # we received */
his_rseq, /* Last good packet # HE received */
wseq; /* Next packet # we will send */
int last_op; /* Last data op: OP_READ or OP_WRITE */
#define OP_READ 0
#define OP_WRITE 1
int reject; /* Packet # to reject or NOREJECT */
#define NOREJECT -1
/* Segments are encoded as (log2 length) - 3, except in INITB packet */
#ifdef BUFFEREDIO
int wndsiz = 7; /* Ask them to send up to 7 packets */
#else
int wndsiz = 1; /* Ask for window of 1 messages flying */
#endif
int segsiz = 2; /* Ask for 64 byte messages */
int sendseg = 2; /* Size segments other guy wants to see */
int sendwin = 1; /* Size window other guy wants to see */
int sendbytes; /* sendseg, in bytes */
int segbytes[10] = { /* K value (encoded segment size) */
-1, /* 0 */
32, /* 1 */
64, /* 2 */
128, /* 3 */
256, /* 4 */
512, /* 5 */
1024, /* 6 */
2048, /* 7 */
4096, /* 8 */
0, /* 9 = KCONTROL */
};
/*
* Low level output routines. These send packets without checking
* whether they got properly received.
*
* writeframe():
*
* Finish off an outgoing frame in msgo and queue it up to be written
* to the serial port.
*
* This routine is called to send each and every packet.
*/
int
writeframe(cksm)
int cksm;
{
msgo[0] = DLE;
msgo[2] = cksm;
msgo[3] = cksm >> 8;
msgo[5] = msgo[1] ^ msgo[2] ^ msgo[3] ^ msgo[4];
if (DEBUG_LEVEL(9)) {
int i, maxlen;
printf("T ");
maxlen = segbytes[msgo[LENBYTE]] + 6;
for (i = 0; i < maxlen; i++)
printf("%02x ",msgo[i] & 0xFF);
putchar('\n');
}
if (DEBUG_LEVEL(6)) {
int t, x, y;
t = msgo[CBYTE] >> 6;
x = (msgo[CBYTE] >> 3) & 7;
y = msgo[CBYTE] & 7;
if ((long)t == (long)CONTROL)
printf("Sent: CONTROL %s %d\n",
ctrl_pr[x], y);
else
printf("Sent: %s %d %d\n",
tt_pr[t], x, y);
}
/*
* In our window=1 implementation, we just queue the packet
* up for transmission here (by leaving it in msgo[]). It
* will be written next time we go through inpkt().
*/
last_op = OP_WRITE; /* Remember to avoid overwriting the packet */
return 0; /* Never aborts */
}
/* Send an ack */
int
ackmsg()
{
msgo[1] = KCONTROL;
if ((long)reject != (long)NOREJECT)
msgo[4] = (CONTROL << 6) | (RJ << 3) | reject;
else
msgo[4] = (CONTROL << 6) | (RR << 3) | rseq;
reject = NOREJECT;
return writeframe((int)(MAGIC - msgo[4]));
}
/* Send a control message other than an ack */
int
ctlmsg(byte)
char byte;
{
msgo[1] = KCONTROL;
msgo[4] = (CONTROL << 6) | byte;
return writeframe((int)(MAGIC - msgo[4]));
}
/*
* Medium level output routine. This sends a short or long data packet
* and figures out when to retransmit and/or insert acknowledgements as
* needed.
*/
sendpacket(s, n, sorl)
char *s;
int n;
int sorl; /* SHORTDATA or LONGDATA */
{
int cksm, offset, difflen;
if ((long)last_op == (long)OP_WRITE) {
/* Better get the first one sent and ack'd first */
/* FIXME, this will change for window > 1 */
if (inpkt()) return 1;
}
bzero((char *)(msgo+6), sendbytes);
msgo[1] = sendseg;
msgo[4] = (sorl << 6) + (wseq << 3) + rseq;
switch(sorl) {
case LONGDATA:
if (n > sendbytes)
{
logit("PROTOCOL VIOLATION IN SENDPACKET", "n > sendbytes");
exit(1);
}
offset = 6;
break;
case SHORTDATA:
difflen = sendbytes - n;
if (difflen < 1)
{
logit("PROTOCOL VIOLATION IN SENDPACKET", "difflen < 1");
exit(1);
}
offset = 7;
if (difflen <= 127) {
msgo[6] = difflen; /* One byte count */
} else {
msgo[6] = 128 | difflen; /* low byte, with 0x80 on */
msgo[7] = difflen >> 7; /* High byte */
offset = 8;
}
}
bcopy(s, (char *)msgo+offset, n); /* Move the data */
cksm = MAGIC - (chksum(&msgo[6], sendbytes) ^ (0377 & msgo[4]));
wseq = (wseq + 1) & 7; /* Bump sent pkt sequence # */
return writeframe(cksm);
}
/*
* Medium level input routine.
*
* Write a packet to the serial port, then read a packet from the serial port.
* Return 1 if other side went away, 0 if good packet received.
*
* With window size of 1, we send a packet and then receive one.
* FIXME, when we implement a larger window size, this routine will become
* more complicated and callers will not be able to depend on msgo[]
* being sent and acknowledged when it returns.
*/
int
inpkt()
{
int data,count,need;
register int i;
unsigned short pktsum, oursum;
int status;
/*
* Next vars are for re-queueing received chars to rescan them
* for a valid packet after an error.
*/
int queued = -1; /* <0: off, 0: just finished, >0: # chars pending */
unsigned char *qp;
unsigned char qbuf[sizeof msgi]; /* This can be static if 4K
on the stack is too much */
# define bad(str) {if (DEBUG_LEVEL(5)) printf str; goto oops; }
if (firstslave) {
firstslave = 0;
goto again;
}
xmit:
i = segbytes[msgo[LENBYTE]] + 6;
status = xwrite(0, (char *)msgo,i);
if ((long)status != (long)i) {
if (DEBUG_LEVEL(0)) {
printf("xmit %d bytes failed, status %d, errno %d",
i, status, errno);
}
return 1; /* Write failed */
}
again:
count = 0;
DEBUG(9, "R ", 0);
while (1) {
if (queued >= 0) {
/*
* Process some stuff from a string.
* If we just finished the last char queued, and
* we are still scanning for a DLE, re-xmit our
* last packet before we continue reading.
* On the other hand, if we have a valid packet
* header accumulating, just keep reading the serial
* port.
*/
if (--queued < 0) {
if (count == 0) {
DEBUG(6, "End queue. Re-xmit.\n", 0);
goto xmit; /* No packet comin' in */
} else {
DEBUG(6,"End queue. Keep reading\n",0);
goto readser; /* Seems to be sumpin' */
}
}
data = *qp++; /* Just grab from queue */
} else {
readser:
data = xgetc();
if (data == EOF) break;
}
if (DEBUG_LEVEL(9))
printf("%02x%c ",data & 0xFF, isprint(data)? data: ' ');
switch (count)
{
case 0:
/* Look for DLE */
if (data == DLE)
msgi[count++] = DLE;
break;
case LENBYTE:
/* Check length byte */
if (data > KCONTROL || data == 0)
bad(("packet size"));
if (segbytes[data] > MAX_PACKET) {
bad(("packet too long for buffer"));
oops:
if (DEBUG_LEVEL(5))
printf(" bad in above packet\n");
/* FIXME, decode packet header here,
if enough of it has come in. */
/* See if any DLEs in the bad packet */
/* Skip 0, we know that's a DLE */
for (i = 1; i < count; i++) {
if (msgi[i] == DLE) {
/* Reprocess from the DLE.
* if queued, back up the q.
* if not, make one.
*/
if (queued >= 0) {
queued += count - i;
qp -= count - i;
DEBUG(6,"Backup and rescan queue\n", 0);
} else {
bcopy((char *)msgi+i, (char *)qbuf,
count - i);
qp = qbuf;
queued = count - i;
DEBUG(6,"Queue and rescan input\n", 0);
}
goto again;
}
}
if (queued >= 0) {
DEBUG(6, "Continue scan\n", 0);
goto again;
} else {
DEBUG(6, "Re-xmit previous packet\n",0);
goto xmit; /* Xmit then rcv */
}
}
msgi[count++] = (unsigned char)data; /* Save it */
msgsize = segbytes[data]; /* Save Packet size */
need = 6 + msgsize;
break;
case CBYTE:
/* Break up control byte as well as storing it */
msgi[count++] = (unsigned char)data; /* Save it */
tt = (data >> 6) & 3;
xxx = (data >> 3) & 7;
yyy = data & 7;
/* Now check it a bit */
switch (tt) { /* Switch on msg type */
case CONTROL:
/* Control msg must have KCONTROL size */
if ((long)msgsize != (long)0) bad(("K versus Control"));
/* We don't implement SRJ, nor does Unix */
switch (xxx) {
case SRJ:
bad(("SRJ received"));
case RJ:
case RR:
if ((long)yyy != (long)(7 & (wseq - 1)))
bad(("didn't ack our pkt"));
}
break;
case ALTCHN:
bad(("ALTCHN received")); /* Unsupported */
case SHORTDATA:
case LONGDATA:
if (msgsize == 0) bad (("KCONTROL with data"));
if (((xxx - rseq) & 7) > wndsiz) {
/* Atari ST cpp has problems with
* macro args broken across lines?
* That's why funny indent here
*/
bad (("data out of window, xxx=%d rseq=%d", xxx, rseq));
}
/* FIXME, below enforces window size == 1 */
/* Note that this is also how we guarantee
that msgo has been received OK by the time
we exit inpkt() too. Don't change it unless
you know what you are doing. */
if ((long)yyy != (long)(7 & (wseq - 1)))
bad(("didn't ack our pkt"));
break;
}
break;
case FRAMEBYTE:
/* See whole frame, check it a bit. */
msgi[count++] = (unsigned char)data;
if ((long)data != (long)(msgi[1] ^ msgi[2] ^ msgi[3] ^ msgi[4]))
bad(("frame checksum"));
pktsum = msgi[2] + (msgi[3] << 8);
if (tt == CONTROL) {
/* Check checksums for control packets */
oursum = MAGIC - msgi[4];
if ((long)pktsum != (long)oursum)
bad(("control checksum"));
/*
* We have a full control packet.
* Update received seq number for the ones
* that carry one.
*/
switch (xxx) {
case RJ: case RR:
if (((wseq - yyy) & 7) > sendwin) {
bad (("RJ/RR out of window, yyy=%d wseq=%d", yyy, wseq));
}
his_rseq = yyy;
}
goto done;
} else {
/*
* Received frame of data packet.
*
* Now that the checksum has been verified,
* we can believe the acknowledgement (if
* any) in it.
*/
if (((wseq - yyy) & 7) > sendwin) {
bad (("data ack out of window, yyy=%d wseq=%d", yyy, wseq));
}
his_rseq = yyy;
}
break;
default:
msgi[count++] = (unsigned char)data;
if (count >= need) {
/* We have received a full data packet */
oursum = MAGIC - (chksum(&msgi[6], sendbytes)
^ (0377 & msgi[4]));
if ((long)pktsum != (long)oursum) {
/* Send a reject on this pkt */
reject = xxx - 1;
bad(("\ndata checksum in packet %x, ours=%x", pktsum, oursum));
}
/* FIXME, this may change for window>1 */
if ((long)xxx != (long)(rseq+1)%8 ) {
bad(("Not next packet xxx=%d rseq=%d", xxx, rseq));
}
rseq = xxx; /* We saw this pkt OK */
done:
if (DEBUG_LEVEL(9))
putchar('\n');
if (DEBUG_LEVEL(6)) {
if (tt == CONTROL)
printf("Rcvd: CONTROL %s %d\n",
ctrl_pr[xxx], yyy);
else
printf("Rcvd: %s %d %d\n",
tt_pr[tt], xxx, yyy);
}
last_op = OP_READ;
return(0);
}
break;
}
}
DEBUG(6, " EOF\n", 0);
return(1);
}
int
chksum(s,n)
register unsigned char *s;
register n;
{
register short sum;
register unsigned short t;
register short x;
sum = -1;
x = 0;
do {
if (sum < 0)
{
sum <<= 1;
sum++;
}
else
sum <<= 1;
t = sum;
sum += *s++ & 0377;
x += sum ^ n;
if ((unsigned short)sum <= t)
sum ^= x;
} while (--n > 0);
return(sum);
}
/*
* Medium level packet driver input routine.
*
* Read a data packet from the other side. If called twice in succession,
* we send an ack of the previous packet. Otherwise we tend to piggyback
* the acks on data packets.
*
* Result is 0 if we got a data packet, 1 if we got some other kind, or
* a hangup timeout.
*/
int
indata()
{
while (1) {
if (last_op == OP_READ) {
ackmsg(); /* Send an ack */
}
if (inpkt()) return 1;
switch (tt) {
case ALTCHN:
return 1; /* Unsupported - yet */
case LONGDATA:
case SHORTDATA:
/*
* We got a data packet. That's what we want,
* so return.
*/
return 0; /* We are done. */
case CONTROL:
switch (xxx) {
default:
return 1; /* Bad packet type */
case RJ: /* Reject prev pkt */
case RR: /* OK but no data */
break; /* Ack and try again */
}
}
}
}
/*
* Open a conversation in the g protocol. Medium level routine.
*
* We need to both send and receive each packet type in turn.
* If this sequence falters, we start over at INITA, trying a few more times.
*
* Returns 0 for success, 1 for failure.
*/
int
gturnon(mastermode)
int mastermode;
{
int tries = 0;
int step = 0;
static int which[] = {INITA, INITA, INITB, INITB, INITC, INITC};
/* initialize protocol globals, e.g. packet sequence numbers */
rseq = 0; /* Last good packet # we have seen from him */
wseq = 1; /* Next packet # we will send */
his_rseq = 0; /* Last good Packet # he has seen from us */
reject = NOREJECT; /* Don't reject first packet */
firstslave = mastermode? 0: 1; /* About to do first slave packet? */
if (mastermode) goto master_start;
while (++tries <= 8) {
/* Receive an initialization packet and handle it */
if (inpkt() == 0 && tt == CONTROL && xxx == which[step]) {
switch (xxx) {
/* Remember we've seen it, grab value */
case INITA:
do_inita:
sendwin = yyy;
break;
case INITB:
/*
* Get preferred packet size for other guy,
* but don't overrun our buffer space.
* The encoded segment size is off-by-1 from
* the one used in the K field in each packet.
*/
do {
sendseg = yyy+1;
sendbytes = segbytes[sendseg];
} while (sendbytes > MAX_PACKET && --yyy);
break;
case INITC:
sendwin = yyy;
break;
}
if (++step >= 6) /* Move forward */
return 0; /* We are done */
} else {
/* Start over, sigh */
step = 0;
if (xxx == INITA) /* Start with his inita */
goto do_inita;
}
master_start:
/*
* Transmit an initialization packet.
*/
switch (which[step]) {
case INITA:
ctlmsg((INITA << 3) | wndsiz);
break;
case INITB:
ctlmsg((INITB << 3) | (segsiz - 1));
break;
case INITC:
ctlmsg((INITC << 3) | wndsiz);
break;
}
if (++step >= 6)
return 0; /* We are done */
}
return 1; /* Failure */
}
/*
* Turn off conversation in the G protocol. Medium level routine.
*/
int
gturnoff()
{
/* In windowed protocol, we have to check if prev one's been ack'd */
int i;
if (last_op == OP_WRITE) {
if (inpkt()) return 1;
}
/* do */
{
ctlmsg(CLOSE << 3);
i = segbytes[msgo[LENBYTE]] + 6;
xwrite(0, (char *)msgo,i);
}
/* while ((tt != CONTROL) && (xxx != CLOSE)); */
return 0;
}
/*
* Read a message from the other side.
*
* Messages are always contained in LONGDATA packets. If a message is
* longer than a single packet, only the last packet will contain a null
* character. Keep catenating them until you see the null.
*
* Return SUCCESS or FAIL. If the received message is longer than our
* buffer, we eat the whole message, but return FAIL once it ends.
*/
int
grdmsg(str, fn)
char *str; /* Buffer to put it in, sized MAXMSGLEN */
int fn; /* File descriptor to read it from */
{
unsigned msglen;
unsigned totlen = 0;
unsigned oldlen;
str[0] = '\0'; /* No command yet */
do {
if ((long)(indata()) || (long)tt != (long)LONGDATA)
return FAIL;
msgi[6+msgsize] = '\0'; /* Null terminate packet */
msglen = strlen((char *)msgi+6);
oldlen = totlen;
totlen += msglen;
if (totlen < MAXMSGLEN) {
strcat(str+oldlen,(char *)&msgi[6]); /* Tack on to command */
}
} while (msglen == msgsize); /* Loop if no null in pkt */
return (totlen < MAXMSGLEN)? SUCCESS: FAIL;
}
/*
* Write a message to the other side.
*
* We write the first (or only) packet from our local bufr[], the
* rest comes straight out of the caller's string.
*/
int
gwrmsg(type, str, fn)
char type;
char *str;
int fn;
{
char bufr[MAX_PACKET];
char *ptr;
int thislen, totlen;
bufr[0] = type;
totlen = strlen(str) + 1;
if (totlen > MAXMSGLEN)
{
logit("PROTOCOL VIOLATION IN GWRMSG", "totlen > MAXMSGLEN");
exit(1);
}
strncpy(&bufr[1], str, sendbytes-1);
ptr = bufr;
for (;;) {
thislen = totlen;
if (thislen > sendbytes) thislen = sendbytes;
if (sendpacket(ptr, thislen, LONGDATA))
return FAIL;
totlen -= sendbytes;
if (totlen < 0) break;
if (ptr == bufr) ptr = str + (sendbytes - 1);
else ptr += sendbytes;
}
return SUCCESS;
}
/*
* Write a file to the other side.
*/
int
gwrdata(file, fn)
FILE *file; /* File to be sent */
int fn; /* fd of serial line */
{
char dskbuf[MAX_PACKET]; /* Disk I/O buffer */
int count;
do {
count = fread(dskbuf, sizeof (char), sendbytes, file);
if (count < 0) return FAIL; /* FIXME, should send EOF */
if (sendpacket(dskbuf, count,
(count == sendbytes)? LONGDATA: SHORTDATA))
return FAIL;
} while (count);
return SUCCESS;
}
/*
* Read a file from the other side.
*/
int
grddata(fn, file)
int fn; /* File desc of serial line */
FILE *file; /* stdio file ptr of file to read into */
{
int offset;
int status;
int error = 0;
do {
/* Read a packet, handle the data in it */
if (indata())
return FAIL;
switch (tt) {
case LONGDATA:
/* FIXME, check this write */
offset = 6;
goto writeit;
case SHORTDATA:
if (msgi[6] & 0x80) {
msgsize -=
(msgi[7] << 7) | (127&msgi[6]);
offset = 8;
} else {
msgsize -= msgi[6];
offset = 7;
}
writeit:
/* FIXME:
* Consider skipping further writing if error != 0 */
if ((long)msgsize != (long)0) {
status = fwrite((char *)&msgi[offset], sizeof (char),
msgsize, file);
if ((long)status != (long)msgsize) error++;
}
break;
}
} while ((long)msgsize != (long)0);
return error? FAIL: SUCCESS;
}